Repository navigation
fix(azure): preserve membership consistency - #11309
Conversation
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
Reuse the fenced cleanup snapshot across successful batches to avoid repeated full scans.
Review effort: Lite
Findings: None
What changed in this PR
Strengthens Azure membership consistency under concurrent reads, liveness updates, and cleanup.
Changes:
- Adds ETag-fenced membership reads and conditional liveness writes.
- Restricts cleanup to stale Dead entries.
- Expands Azure SDK regression coverage and shared cleanup tests.
| File | Description |
|---|---|
test/Orleans.Runtime.Internal.Tests/MembershipTests/MembershipTableTestsBase.cs |
Updates shared cleanup expectations. |
test/Extensions/Orleans.Azure.Tests/AzureMembershipPaginationTests.cs |
Adds read, write, cleanup, cancellation, and error regressions. |
src/Azure/Orleans.Clustering.AzureStorage/OrleansSiloInstanceManager.cs |
Implements fencing, conditional writes, and recency-based cleanup; cleanup rescans the full partition for each successful batch, causing unnecessary O(N²/B) read traffic. |
src/Azure/Orleans.Clustering.AzureStorage/AzureBasedMembershipTable.cs |
Surfaces malformed membership rows. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Code coverage
Report-only conclusion: current-main baseline stale. The newest successful coverage run tested 5267011, not current main 53b0307. Coverage combines every CI test matrix job, including providers, CodeGen, .NET 8/10, Linux, Windows, and macOS, using canonical physical source and branch identities. The comparison remains report-only while normal line and branch variance is calibrated. Coverage details |
|
Addressed the cleanup review in e4458f6. Cleanup now passes the selected rows to the existing DeleteEntriesBatch helper, reusing one fenced snapshot across all successful batches. Row-etag contention triggers a fresh selection after outstanding batches complete. Conditional deletions continue to protect concurrent heartbeats and votes. The regression checks exactly one partition query and two version reads for 195 deletions with batch limits 1, 2, 3, and 100. The contention case now covers a successful batch followed by a conflicting batch, then confirms that refreshed liveness prevents deletion on retry. I also consolidated repeated heartbeat-failure setup and empty/ineligible-cleanup cases, and reused the existing SDK mock factory. The PR is 57 net lines smaller while retaining all 76 focused cases. The net10.0 build and focused suite pass. The small shared cleanup-test prerequisite remains necessary until #11308 lands; rebasing afterward will remove that fourth file from this PR's diff. |
|
Follow-up to #11309 (review): the summary had no inline details, but the failure-handling audit identified and reproduced two concrete gaps. Fixed in 38cf2de:
Regressions: CleanupPropagatesMixedBatchFailures, CleanupRetriesWhenEveryBatchFailureIsContention, and UpdateDistinguishesMissingRowFromMissingTableThroughSdk. The latter uses the actual TableClient with an in-memory HTTP transport, exercising SDK 404 classification. Existing tests continue to cover valid cleanup batch limits 1/2/3/100, recency/cutoff equality, cancellation, and row-etag safety; the review summary supplied no additional concrete validation requirement. The new regressions reproduced five failures before the fix. The revised focused net10.0 class passes 84/84, with a clean build. Both Azure Storage CI jobs must pass on this exact head before this follow-up is considered complete. The PR remains draft. |
|
The required Azure CI gate is now satisfied for exact head
This supplements the 84/84 focused regression run and clean local build. The PR remains draft; other checks are still running. |
|
Simplified heartbeat writes in Removed the unnecessary heartbeat read/max/CAS retry loop and its stronger multi-writer assumptions from the tests. Full-row membership updates retain their separate protection against overwriting a newer heartbeat. The patch removes 66 net lines.
|
|
Applied the clarified owner-only heartbeat policy in
Focused net10.0 result: 78 passed, zero failed/skipped; build: zero warnings/errors. PR description updated. Validation now follows this exact head; awaiting both Azure Storage jobs and remaining checks. |
|
Applied the clarified heartbeat-independent concurrency contract in ReadRow/ReadAll now expose the canonical table-version etag as the opaque membership row token. UpdateRow validates logical row/table token agreement, then issues one atomic transaction using wildcard UpdateReplace for the existing silo row and CAS for the canonical version row. Removed the row preread, heartbeat maximum comparison, and physical heartbeat-etag dependency. IAmAlive follows the accepted best-effort semantics; heartbeat publication remains one blind column merge. Raw storage etags remain internal for conditional cleanup.
Focused net10.0 validation: 78 passed, zero failed/skipped; clean build. PR description reflects the latest contract. Awaiting exact-head Azure Storage net8/net10 and remaining CI checks. |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved critical and moderate findings remain in membership updates, cleanup filtering, and token-related test expectations.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (1)
|
Audited review #11309 (review). Its sole linked finding asks for stored-heartbeat maximum preservation, which the final agreed contract explicitly supersedes. Answered and resolved that thread with the updated #11308 contract and the native accepted-overwrite regression: #11309 (comment). The overview's additional references to cleanup filtering and token test expectations provide no separate location or reproduction. Current focused regressions cover Dead-only/recency/cutoff filtering, conditional cleanup races, canonical-token stability across heartbeats, and true stale-token/missing-row failures. There is no additional concrete change to make from those phrases. The latest 78-case focused suite passes; exact-head CI remains the completion gate. No code change was needed for this contract clarification. |
|
Final-head validation completed for
The heartbeat-preservation review thread is answered and resolved against the final canonical-token contract. The branch/worktree is clean; no merge was performed. |

Problem
Heartbeat-only writes change physical Azure row etags, creating false contention for membership updates. Cleanup currently selects entries using Azure's storage timestamp rather than membership status and latest known activity.
Solution
!Startand~Endmarkers already written atomically with membership changes for paginated ReadAll queries. Matching marker/canonical versions establish consistency; mismatches retry within the existing bound and missing markers fail explicitly. ReadRow retains its existing single filtered query for the requested member and canonical version row.TaskUtilities.WhenAllWithAggregateExceptionhelper awaits every task, propagates single faults and cancellation normally, and usesExceptionDispatchInfo.Throwto expose multiple failures as an aggregate. Azure cleanup compiles the shared source through a project link; the helper remains internal in each consuming assembly.The first commit is the shared legacy cleanup-test prerequisite from #11308: source
bede893d290a9035f196f4c3ed58213d102efc6c, cherry-picked here as8633ec7a62d83832c8cddb70f0f188848e4abd44. Subsequent commits contain the Azure provider changes and focused regressions, plus the requested internal common task-helper extraction. The generated public API surface matches main.Microsoft Reviewers: Open in CodeFlow